/**
 * RolesService.java
 */
package com.rizzo.web.security;

import org.springframework.core.io.ClassPathResource;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * A facade for retrieving the Roles (possible URL's) for a given LDAP-role
 * retrieved from the SessionContext. It must be initalized with the constructor that contains
 * a path to the filename on the classpath that contains the mapping of roles to functions.
 *
 * @author jalieven
 */
public class RolesService {

    private List<Role> roles;

    /**
     * Constructor that will parse the xml-file with all the roles with it's functions and places it in the
     * roles-attribute of this façade.
     * <p/>
     * TODO cache the roleParser (do it with JAXB in stead of DOM and call the parse-method for every method
     *
     * @param rolesFilename The xml-filename of the roles.
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws RoleParserException
     */
    public RolesService(String rolesFilename) throws IOException, ParserConfigurationException, SAXException, RoleParserException {
        ClassPathResource classPathResource = new ClassPathResource(rolesFilename);
        this.roles = RoleParser.parseRoles(classPathResource.getFile());
    }

    /**
     * Returns the Role that has the specified name.
     *
     * @param roleName a String for which the {@link Role} must be retrieved.
     * @return A {@link Role} for the given roleName.
     */
    public Role getRoleForName(String roleName) {
        Role returnRole = null;
        for (Role role : this.roles) {
            if (roleName != null && roleName.equals(role.getRoleEnum().getFullRoleName())) {
                returnRole = role;
            }
        }
        return returnRole;
    }

    /**
     * Retrieves all the Roles (as defined in the applications roles.xml) for a given array of Strings (as defined in LDAP).
     *
     * @param roleNames
     * @return
     */
    public List<Role> getRolesForNames(String[] roleNames) {
        List<Role> rolesForNames = new ArrayList<Role>();
        // for every roleName in the array of Strings loop through the parsed roles to see if they match
        for (String roleName : roleNames) {
            for (Role role : this.roles) {
                if (roleName.equals(role.getRoleEnum().getFullRoleName())) {
                    if (role.getRoleEnum() == RoleEnum.anonymous) {
                        rolesForNames.add(0, role);
                    } else {
                        rolesForNames.add(role);
                    }
                }
            }
        }
        return rolesForNames;
    }

    /**
     * Method that will run through all the roles and construct a Map<"url", "role,role,role"> with the data from the xml.
     * This method puts in the functions (defined for the navigation-menu) AND the allowed-functions (for security).
     *
     * @return
     */
    public List<RoleUrl> getUrlRolesMap() {
        List<RoleUrl> roleUrlList = new ArrayList<RoleUrl>();
        // run through all the roles and construct a Map<"url", "role,role,role"> with the data from the xml
        for (Role role : roles) {
            // put in the navigation functions
            for (Function function : role.getFunctions()) {
                RoleUrl roleUrl = new RoleUrl(function.getUrl(), role.getRoleEnum().getFullRoleName());
                if (!roleUrlList.contains(roleUrl)) {
                    roleUrlList.add(roleUrl);
                } else {
                    RoleUrl existing = roleUrlList.get(roleUrlList.indexOf(roleUrl));
                    existing.addRole(role.getRoleEnum().getFullRoleName());
                }
            }
            // put in the allowed functions (caution allowed is not mandatory)
            if (role.getAllowed() != null) {
                for (Function allowed : role.getAllowed()) {
                    RoleUrl roleUrl = new RoleUrl(allowed.getUrl(), role.getRoleEnum().getFullRoleName());
                    if (!roleUrlList.contains(roleUrl)) {
                        roleUrlList.add(roleUrl);
                    } else {
                        RoleUrl existing = roleUrlList.get(roleUrlList.indexOf(roleUrl));
                        existing.addRole(role.getRoleEnum().getFullRoleName());
                    }
                }
            }
        }
        return roleUrlList;
    }

    /**
     * Checks if the logged in user has privileges i.e. functions excluding the default Home - index.jsp function.
     *
     * @param roles
     * @return
     */
    public boolean hasPrivileges(String[] roles) {
        boolean retValue = false;
        // if there are no roles == no priviliges
        if (roles.length == 0) {
            retValue = false;
        } else {
            // if there are roles but no functions (excluding the default Home - index.jsp function) defined for those roles == no privileges 
            List<Role> rolesForNames = this.getRolesForNames(roles);
            for (Role rolesForName : rolesForNames) {
                for (Function function : rolesForName.getFunctions()) {
                    if (!"Home".equals(function.getName())) {
                        retValue = true;
                    }
                }
            }
        }
        return retValue;
    }

}